﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;


namespace Week1.particles
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class BaseParticle : Microsoft.Xna.Framework.DrawableGameComponent
    {

        protected player.Camera oCamera;
        protected VertexPositionNormalTexture[] oVertex = new VertexPositionNormalTexture[4];
        protected Matrix[] oWorld;
        protected float[] fRotate;
        protected int iCount = 0;

        helper.DefaultEffect oEffect;
        DepthStencilState oDepthState;
        RasterizerState oRasterState;
        string sTextureName;
        int iPrimitiveSize = 1;

        public Texture2D oTexture;
        public Vector3[] oPosition;
        public bool bUseBillboard = true;
        public bool bUseDepth = true;
        public float iScale = 1;
        public bool bUseCameraSettings = true;

        public BaseParticle(Game game, string textureName, player.Camera camera, int scale, int count)
            : base(game)
        {
            sTextureName = textureName;
            oCamera = camera;
            iPrimitiveSize = scale;
            iCount = count;

            oWorld = new Matrix[count];
            oPosition = new Vector3[count];
            fRotate = new float[count];

        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            oVertex[0] = new VertexPositionNormalTexture(new Vector3(-iPrimitiveSize, iPrimitiveSize, 0), new Vector3(0, 0, -1), new Vector2(0));
            oVertex[1] = new VertexPositionNormalTexture(new Vector3(iPrimitiveSize, iPrimitiveSize, 0), new Vector3(0, 0, -1), new Vector2(1, 0));
            oVertex[2] = new VertexPositionNormalTexture(new Vector3(-iPrimitiveSize, -iPrimitiveSize, 0), new Vector3(0, 0, -1), new Vector2(0, 1));
            oVertex[3] = new VertexPositionNormalTexture(new Vector3(iPrimitiveSize, -iPrimitiveSize, 0), new Vector3(0, 0, -1), new Vector2(1));

            // atleast repeat 1 time, otherwise nothing will draw.
            if (iCount == 0)
            {
                iCount = 1;
                oPosition[0] = Vector3.Zero;
                if (bUseBillboard && bUseCameraSettings)
                    oWorld[0] = Matrix.CreateBillboard(oPosition[0], oCamera.Position, Vector3.Up, Vector3.Forward);
                else
                    oWorld[0] = Matrix.CreateWorld(oPosition[0], Vector3.Forward, Vector3.Up);
            }

            base.Initialize();
        }


        /// <summary>
        /// 
        /// </summary>
        protected override void LoadContent()
        {
            
            if (sTextureName != null)
                oTexture = this.Game.Content.Load<Texture2D>(sTextureName);
            
            //oVertBuffer = new VertexBuffer(this.GraphicsDevice, typeof(VertexPositionNormalTexture), oVertex.Length, BufferUsage.None);
            //oVertBuffer.SetData<VertexPositionNormalTexture>(oVertex);
            
            // load default effect that clips transparent pixels
            oEffect = new helper.DefaultEffect(this.Game);
            
            oDepthState = new DepthStencilState();
            oDepthState.DepthBufferEnable = bUseDepth;

            oRasterState = new RasterizerState();
            oRasterState.CullMode = CullMode.None;
            
            base.LoadContent();
        }

        /// <summary>0
        /// 
        /// </summary>
        /// <param name="gameTime"></param>
        public override void Update(GameTime gameTime)
        {
            if (bUseBillboard && bUseCameraSettings)
                oWorld[0] = Matrix.CreateScale(iScale) * Matrix.CreateBillboard(oPosition[0], oCamera.Position * -1, Vector3.Up, Vector3.Forward);
            else if (bUseCameraSettings)
                oWorld[0] = Matrix.CreateScale(iScale) * Matrix.CreateWorld(oPosition[0], Vector3.Forward, Vector3.Up);
            else
                oWorld[0] = Matrix.Identity;


            base.Update(gameTime);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="gameTime"></param>
        public override void Draw(GameTime gameTime)
        {
            //oEffect.EnableDefaultLighting();
            this.GraphicsDevice.DepthStencilState = oDepthState;
            this.GraphicsDevice.RasterizerState = oRasterState;
            
            this.GraphicsDevice.BlendState = BlendState.AlphaBlend;
            
            oEffect.Texture = oTexture;
            oEffect.TextureEnabled = true;

            if (bUseCameraSettings)
            {
                oEffect.Projection = oCamera.Projection;
                oEffect.View = oCamera.View;
            }
            else 
            {
                oEffect.Projection = Matrix.Identity;
                oEffect.View = Matrix.Identity;
            }
            
            for (int iIdx = 0; iIdx < iCount; iIdx++)
            {
                oEffect.World = oWorld[iIdx];

                EffectPass oPass = oEffect.CurrentTechnique.Passes[0];
                oPass.Apply();
                GraphicsDevice.DrawUserPrimitives<VertexPositionNormalTexture>(PrimitiveType.TriangleStrip, oVertex, 0, 2);
            }

            this.GraphicsDevice.BlendState = BlendState.Opaque;
            
            base.Draw(gameTime);
        }
    }
}